package com.yeziji.utils.expansion;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.yeziji.common.CommonSymbol;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Array;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

/**
 * List 工具包
 *
 * @author hwy
 * @since 2023/09/06 0:24
 **/
public class Lists2 {
    /**
     * 转换对象
     *
     * @param list      转换原列表
     * @param transform 转换函数
     * @param <T>       原泛型
     * @param <O>       目标泛型
     * @return {@link List} 新的泛型列表
     */
    public static <T, O> List<O> transforms(Collection<T> list, Function<T, O> transform) {
        if (isEmpty(list)) {
            return empty();
        }

        return list.stream().map(transform).collect(Collectors.toList());
    }

    /**
     * 將數組轉換為目標列表
     *
     * @param arr       转换数组
     * @param transform 转换函数
     * @param <T>       原泛型
     * @param <O>       目标泛型
     * @return {@link List} 新的泛型列表
     */
    public static <T, O> List<O> transforms(T[] arr, Function<T, O> transform) {
        if (arr == null || arr.length == 0) {
            return Collections.emptyList();
        }

        return Arrays.stream(arr).map(transform).collect(Collectors.toList());
    }

    /**
     * 转换为 Set
     *
     * @param list      转换原列表
     * @param transform 转换函数
     * @param <T>       原泛型
     * @param <O>       目标泛型
     * @return {@link List} 新的泛型列表
     */
    public static <T, O> Set<O> transformSet(Collection<T> list, Function<T, O> transform) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptySet();
        }

        return list.stream().map(transform).collect(Collectors.toSet());
    }

    /**
     * 將内部 list 拆分出来
     *
     * @param list      含 list 的列表
     * @param transform 拆分函数
     * @param <T>       原泛型
     * @param <O>       目标泛型
     * @return {@link List} 拆分出来的列表
     */
    public static <T, O> List<O> nestsTransforms(Collection<T> list, Function<T, List<O>> transform) {
        return nests(Lists2.transforms(list, transform));
    }

    /**
     * 將内部 list 拆分出来
     *
     * @param list 含 list 的列表
     * @param <T>  原泛型
     * @return {@link List} 拆分出来的列表
     */
    public static <T> List<T> nests(Collection<List<T>> list) {
        if (isEmpty(list)) {
            return empty();
        }

        return list.stream().flatMap(Collection::stream).collect(toList());
    }

    /**
     * 判断列表是否为 null 或者为空集合
     *
     * @param list 列表
     * @param <T>  任意泛型列表
     * @return {@link Boolean}
     */
    public static <T> boolean isEmpty(Collection<T> list) {
        return list == null || list.isEmpty();
    }

    /**
     * 判断列表是否不为 null 或者不为空集合
     *
     * @param list 列表
     * @param <T>  任意泛型列表
     * @return {@link Boolean}
     */
    public static <T> boolean isNotEmpty(Collection<T> list) {
        return !isEmpty(list);
    }

    /**
     * 不为空的后续消费操作
     *
     * @param list     判断列表
     * @param consumer 消费行为
     * @param <T>      任意泛型
     */
    public static <T> void isNotEmptyThen(Collection<T> list, Consumer<Collection<T>> consumer) {
        if (isNotEmpty(list)) {
            consumer.accept(list);
        }
    }

    /**
     * 以 , 分割出新的列表
     *
     * @param split 分割字符串
     * @return {@link List}
     * @see #splitter(String, String)
     */
    public static List<String> splitterCommaStr(String split) {
        return splitter(CommonSymbol.COMMA, split);
    }

    /**
     * 根据指定通配符分割为列表
     *
     * @param splitter 分割标识符
     * @param splitStr 分割字符串
     * @return {@link List}
     */
    public static List<String> splitter(String splitter, String splitStr) {
        if (StrUtil.isBlank(splitStr) || StrUtil.isBlank(splitter)) {
            return Collections.emptyList();
        }

        return Splitter.on(splitter).splitToList(splitStr);
    }

    /**
     * 根据逗号将列表转换为字符串
     *
     * @param list 列表
     * @param <T>  任意泛型
     * @return {@link String} 字符串
     */
    public static <T> String joinerCommaStr(Collection<T> list) {
        return joiner(CommonSymbol.COMMA, list);
    }

    /**
     * 根据分隔符将列表转换为字符串
     *
     * @param splitter 分隔符
     * @param list     列表
     * @param <T>      任意泛型
     * @return {@link String} 字符串
     */
    public static <T> String joiner(String splitter, Collection<T> list) {
        if (isEmpty(list)) {
            return StringUtils.EMPTY;
        }

        return Joiner.on(splitter).join(list);
    }

    /**
     * 将任意列表转化为 List
     *
     * @param list 待转换列表
     * @param <T>  人已返现
     * @return {@link List}
     */
    public static <T> List<T> emptyIfNull(Collection<T> list) {
        if (isEmpty(list)) {
            return empty();
        }

        return Optional.of(Lists.newArrayList(list)).orElseGet(ArrayList::new);
    }

    /**
     * 空集合
     *
     * @param <T> 人已返现
     * @return {@link List}
     */
    public static <T> List<T> empty() {
        return Lists.newArrayList();
    }

    /**
     * 获取第一个元素，如果获取不到返回 null
     *
     * @param list 获取的列表
     * @param <T>  任意泛型
     * @return object
     */
    public static <T> T getFirst(Collection<T> list) {
        return getFirstOpt(list).orElse(null);
    }

    /**
     * 获取第一个元素返回 Optional {@link Stream#findFirst()}
     *
     * @param list 获取的列表
     * @param <T>  任意泛型
     * @return {@link Optional}
     */
    public static <T> Optional<T> getFirstOpt(Collection<T> list) {
        if (isEmpty(list)) {
            return Optional.empty();
        }

        return list.stream().findFirst();
    }

    /**
     * 过滤后获取第一个元素
     *
     * @param list      获取列表
     * @param predicate 过滤条件
     * @param <T>       任意泛型
     * @return {@link Optional}
     */
    public static <T> Optional<T> filterFirstOpt(Collection<T> list, Predicate<T> predicate) {
        if (isEmpty(list)) {
            return Optional.empty();
        }

        return list.stream().filter(predicate).findFirst();
    }

    /**
     * 过滤后获取第一个元素
     *
     * @param array     获取数组
     * @param predicate 过滤条件
     * @param <T>       任意泛型
     * @return {@link Optional}
     */
    public static <T> Optional<T> filterFirstOpt(T[] array, Predicate<T> predicate) {
        if (array == null || array.length == 0) {
            return Optional.empty();
        }
        return filterFirstOpt(Lists.newArrayList(array), predicate);
    }

    /**
     * 根据 function 过滤与对比组的数据差异
     *
     * @param list      参照组
     * @param contrasts 对比组
     * @param apply     执行对比的函数
     * @param <O>       对比组泛型
     * @return {@link List} 在对比组中不存在的数据
     */
    public static <T, O> List<T> diffList(Collection<T> list, Collection<O> contrasts, Function<T, O> apply) {
        if (isEmpty(list) || isEmpty(contrasts)) {
            return Lists.newArrayList(list);
        }
        if (apply == null) {
            return Lists.newArrayList(list);
        }

        return list.stream()
                .filter(td -> contrasts.stream().noneMatch(contrast -> contrast.equals(apply.apply(td))))
                .collect(toList());
    }

    /**
     * 根据 function 过滤与对比组的数据差异
     *
     * @param list      参照组
     * @param contrasts 对比组
     * @param apply     执行对比的函数
     * @param <O>       对比组泛型
     * @return {@link List} 在对比组中不存在的数据
     */
    public static <T, O> List<O> diffContrastList(Collection<T> list, Collection<O> contrasts, Function<T, O> apply) {
        if (isEmpty(list) || isEmpty(contrasts)) {
            return Lists.newArrayList(contrasts);
        }
        if (apply == null) {
            return Lists.newArrayList(contrasts);
        }

        List<O> remainList = new ArrayList<>();
        contrasts.forEach(contrast -> {
            Set<O> collect = list.stream().map(apply).collect(Collectors.toSet());
            if (!collect.contains(contrast)) {
                remainList.add(contrast);
            }
        });
        return remainList;
    }

    /**
     * list 转 map
     *
     * @param list      list 列表
     * @param keyFunc   map key 函数
     * @param valueFunc map value 函数
     * @param <T>       列表泛型
     * @param <K>       键值泛型
     * @param <V>       对象泛型
     * @return {@link Map}
     */
    public static <T, K, V> Map<K, V> streamToMap(Collection<T> list, Function<T, K> keyFunc, Function<T, V> valueFunc) {
        if (isEmpty(list)) {
            return new HashMap<>();
        }
        if (keyFunc == null || valueFunc == null) {
            return new HashMap<>();
        }

        return list.stream().collect(toMap(keyFunc, valueFunc));
    }

    /**
     * list 转 map
     *
     * @param list    list 列表
     * @param keyFunc map key 函数
     * @param <T>     列表泛型
     * @param <K>     键值泛型
     * @return {@link Map}
     */
    public static <T, K> Map<K, T> streamToMap(Collection<T> list, Function<T, K> keyFunc) {
        if (isEmpty(list)) {
            return new HashMap<>();
        }
        if (keyFunc == null) {
            return new HashMap<>();
        }

        return list.stream().collect(toMap(keyFunc, Function.identity()));
    }

    /**
     * list 转 map
     *
     * @param list    list 列表
     * @param keyFunc map key 函数
     * @param <T>     列表泛型
     * @param <K>     键值泛型
     * @return {@link Map}
     */
    public static <T, K> Map<K, List<T>> streamToGroupBy(Collection<T> list, Function<T, K> keyFunc) {
        if (isEmpty(list)) {
            return new HashMap<>();
        }
        if (keyFunc == null) {
            return new HashMap<>();
        }

        return list.stream().collect(groupingBy(keyFunc));
    }

    /**
     * list 转 map
     *
     * @param list      list 列表
     * @param keyFunc   map key 函数
     * @param valueFunc map value 函数
     * @param <T>       列表泛型
     * @param <K>       键值泛型
     * @param <V>       对象泛型
     * @return {@link Map}
     */
    public static <T, K, V> Map<K, List<V>> streamToGroupBy(Collection<T> list, Function<T, K> keyFunc, Function<T, V> valueFunc) {
        if (isEmpty(list)) {
            return new HashMap<>();
        }
        if (keyFunc == null) {
            return new HashMap<>();
        }

        return list.stream().collect(groupingBy(keyFunc, mapping(valueFunc, toList())));
    }

    /**
     * 克隆扩容，在原数组的基础上追加非 null 的值并进行扩容，返回新的数组
     *
     * @param originArray 原数组
     * @param data        扩容对象
     * @param array       支持多个扩容对象
     * @param <T>         任意泛型
     * @return {@link Array} 新的数组
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] cloneExpansion(T[] originArray, T data, T... array) {
        T[] newArray = expansion(originArray, data, array);
        // 复制旧数组内容
        System.arraycopy(originArray, 0, newArray, 0, originArray.length);
        // 优先追加单个元素
        int offset = originArray.length;
        if (data != null) {
            newArray[offset++] = data;
        }
        // 追加数组元素
        if (array != null) {
            for (int i = 0; i < array.length; i++) {
                if (array[i] != null) {
                    newArray[offset + i] = array[i];
                } else {
                    offset--;
                }
            }
        }
        return newArray;
    }

    /**
     * 數組擴容
     * <p>
     * 过滤掉 null 值，进行扩容
     * </p>
     *
     * @param originArray 待扩容数组
     * @param data        必须传入一个扩容对象
     * @param array       多个扩容对象
     * @param <T>         任意反省
     * @return {@link Array} 新的擴容數組
     */
    @SuppressWarnings("unchecked")
    private static <T> T[] expansion(T[] originArray, T data, T[] array) {
        if (originArray == null) {
            throw new IllegalArgumentException("扩容对象不能为 null");
        }
        // 计算新数组长度
        int newSize = originArray.length;
        if (data != null) {
            newSize += 1;
        }
        if (array != null) {
            for (T t : array) {
                if (t != null) {
                    newSize++;
                }
            }
        }
        // 创建新数组
        return (T[]) Array.newInstance(originArray.getClass().getComponentType(), newSize);
    }

    /**
     * 对比列表
     *
     * @param list         对比列表
     * @param contrastList 参照列表
     * @param <O>          对比泛型
     * @param <T>          参照泛型
     * @return {@link CompareUtil} 对比工具类，用于链式
     */
    public static <O, T> CompareUtil<O, T> compare(Collection<O> list, Collection<T> contrastList) {
        return CompareUtil.init(list, contrastList);
    }

    /**
     * 辅助对比工具
     *
     * @param <O> 原列表泛型
     * @param <T> 对照列表泛型
     */
    public static class CompareUtil<O, T> {
        /**
         * 对比标记, 如果为 true 说明已经对比过了
         */
        private boolean compareTag;

        /**
         * 目标列表
         */
        private List<O> disposeList;

        /**
         * 对比列表
         *
         * <p>可以任意类型, 但是对比时需要泛型转换</p>
         */
        private List<T> contrastList;

        /**
         * 定义对比存在相互的消费者函数
         */
        private BiConsumer<O, Object> existsConsumer;

        /**
         * 记录: 对比发现对照组中不存在, 但是操作列表存在的值
         */
        private final List<O> contrastNotExistsList = new ArrayList<>();

        /**
         * 记录: 对比发现操作列表不存在, 但是对照列表存在的值
         */
        private final List<T> disposeNotExistsList = new ArrayList<>();

        /**
         * 记录: 对比相互存在的 2 个列表
         */
        private final List<O> disposeExistsList = new ArrayList<>();

        private final List<T> contrastExistsList = new ArrayList<>();

        /**
         * 多函数对比方式
         */
        private List<Function<O, Object>> disposeSampleFunctions;

        private List<Function<T, Object>> contrastSampleFunctions;

        private Map<Function<O, Object>, Function<T, Object>> sampleFunctions;

        /**
         * 目标列表不存在的数据
         */
        private Consumer<T> notExistsConsumer;

        /**
         * 对比列表中不存在的数据
         */
        private Consumer<O> contrastNotExistsConsumer;

        // 禁止外部无参构造
        private CompareUtil() {
        }

        /**
         * 初始化对照工具类
         *
         * @param list         操作列表
         * @param contrastList 对照列表
         * @param <O>          对比初始泛型
         * @return {@link CompareUtil} 对比工具类
         */
        public static <O, T> CompareUtil<O, T> init(Collection<O> list, Collection<T> contrastList) {
            CompareUtil<O, T> compareUtil = new CompareUtil<>();
            compareUtil.disposeList = new ArrayList<>(list);
            compareUtil.contrastList = new ArrayList<>(contrastList);
            return compareUtil;
        }

        /**
         * 定义多个对照组函数
         *
         * @param disposeSample  操作对照函数
         * @param contractSample 参照对照函数
         * @return {@link CompareUtil} 构造链
         */
        public final CompareUtil<O, T> addSample(Function<O, Object> disposeSample, Function<T, Object> contractSample) {
            if (this.sampleFunctions == null) {
                this.sampleFunctions = new LinkedHashMap<>();
            }
            this.sampleFunctions.put(disposeSample, contractSample);
            return this;
        }

        /**
         * 定义原列表存在的操作行为
         *
         * @param existsConsumer 消费行为
         * @return {@link CompareUtil} 构造链
         */
        public CompareUtil<O, T> existsDo(BiConsumer<O, Object> existsConsumer) {
            this.existsConsumer = existsConsumer;
            return this;
        }

        /**
         * 定义原列表不存在的操作行为
         *
         * @param notExistsConsumer 消费行为
         * @return {@link CompareUtil} 构造链
         */
        public CompareUtil<O, T> notExistsDo(Consumer<T> notExistsConsumer) {
            this.notExistsConsumer = notExistsConsumer;
            return this;
        }

        /**
         * 定义原列表存在但对比列表不存在的操作行为
         *
         * @param contrastNotExistsConsumer 消费行为
         * @return {@link CompareUtil} 构造链
         */
        public CompareUtil<O, T> contrastNotExistsDo(Consumer<O> contrastNotExistsConsumer) {
            this.contrastNotExistsConsumer = contrastNotExistsConsumer;
            return this;
        }

        /**
         * 执行对比行为
         *
         * @return {@link CompareUtil} 继续返回构造链
         */
        public CompareUtil<O, T> execute() {
            if (this.compareTag) {
                return this;
            }

            // 操作列表为空, 但是对照组不为空
            if (isEmpty(this.disposeList) && isNotEmpty(this.contrastList)) {
                // 操作行为
                if (this.notExistsConsumer != null) {
                    this.contrastList.forEach(this.notExistsConsumer);
                }
                this.disposeNotExistsList.addAll(this.contrastList);
                this.compareTag = true;
                return this;
            }

            // 对照组为空, 操作列表不为空
            if (isEmpty(this.contrastList) &&
                    isNotEmpty(this.disposeList)) {
                // 操作行为
                if (this.contrastNotExistsConsumer != null) {
                    this.disposeList.forEach(this.contrastNotExistsConsumer);
                }
                this.contrastNotExistsList.addAll(this.disposeList);
                this.compareTag = true;
                return this;
            }

            // 先拷贝一份新列表, 用于对比方便减少笛卡尔积
            List<T> copyNewList = Lists.newCopyOnWriteArrayList(this.contrastList);
            int existsCursor = 0;
            for (O obj : this.disposeList) {
                // 记录对应存在的列表
                final int originalSize = existsCursor;
                // 遍历对比组
                for (int j = 0; j < copyNewList.size(); j++) {
                    T contrast = copyNewList.get(j);
                    boolean allTruth = true;
                    // 遍历样本函数
                    if (MapUtil.isNotEmpty(sampleFunctions)) {
                        for (Map.Entry<Function<O, Object>, Function<T, Object>> entry : sampleFunctions.entrySet()) {
                            Function<O, Object> disposeSample = entry.getKey();
                            Function<T, Object> contractSample = entry.getValue();
                            // 参照组的样本
                            Object originalSample = disposeSample.apply(obj);
                            // 对照组的样本
                            Object contrastSample = contractSample.apply(contrast);
                            // 如果不相同则为 false, 并且可以直接跳出不用再对比了
                            if (!Objects.equals(originalSample, contrastSample)) {
                                allTruth = false;
                                break;
                            }
                        }
                    } else {
                        allTruth = Objects.equals(obj, contrast);
                    }
                    // 全部对比通过, 就添加对应存在的列表中
                    if (allTruth) {
                        // 游标下移
                        existsCursor++;
                        // 存在操作
                        if (this.existsConsumer != null) {
                            this.existsConsumer.accept(obj, contrast);
                            // 记录相互存在的列表数据
                            this.disposeExistsList.add(obj);
                            this.contrastExistsList.add(contrast);
                        }
                        // 找到相同的就可以移除从而减少笛卡尔积
                        copyNewList.remove(j--);
                    }
                }
                /*
                    如果 obj 对比了一遍 compare 都没对应的上, 那么 existsCursor 不会发生改变, 那么就认为 obj 和 compare 没有一个相同的;
                    这时侯就认为对照列表中不存在对应的目标值
                 */
                if (originalSize == existsCursor) {
                    this.contrastNotExistsList.add(obj);
                }
            }

            // 到这里, 如果存在对照组还存在值没被消费完的, 说明就是原列表不存在的值;
            if (isNotEmpty(copyNewList)) {
                this.disposeNotExistsList.addAll(copyNewList);
            }

            if (isNotEmpty(this.disposeNotExistsList) && this.notExistsConsumer != null) {
                this.disposeNotExistsList.forEach(this.notExistsConsumer);
            }

            if (Lists2.isNotEmpty(this.contrastNotExistsList) && this.contrastNotExistsConsumer != null) {
                this.contrastNotExistsList.forEach(this.contrastNotExistsConsumer);
            }

            this.compareTag = true;
            return this;
        }

        public void doNotExists(Consumer<List<T>> disposeConsumer, Consumer<List<O>> contrastConsumer) {
            this.execute();
            if (!this.disposeNotExistsList.isEmpty()) {
                disposeConsumer.accept(this.disposeNotExistsList);
            }
            if (!this.contrastNotExistsList.isEmpty()) {
                contrastConsumer.accept(this.contrastNotExistsList);
            }
        }

        public void doExists(Consumer<List<O>> disposeConsumer, Consumer<List<T>> contrastConsumer) {
            this.execute();
            if (!this.disposeExistsList.isEmpty()) {
                disposeConsumer.accept(this.disposeExistsList);
            }
            if (!this.contrastExistsList.isEmpty()) {
                contrastConsumer.accept(this.contrastExistsList);
            }
        }

        public List<O> getContrastNotExistsList() {
            if (!this.compareTag) {
                this.execute();
            }
            return this.contrastNotExistsList;
        }

        public List<T> getDisposeNotExistsList() {
            if (!this.compareTag) {
                this.execute();
            }
            return this.disposeNotExistsList;
        }

        public List<O> getDisposeExistsList() {
            if (!this.compareTag) {
                this.execute();
            }
            return this.disposeExistsList;
        }

        public List<T> getContrastExistsList() {
            if (!this.compareTag) {
                this.execute();
            }
            return this.contrastExistsList;
        }
    }
}
